﻿using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Fast.Extensions.DatabaseAccessor.EntityFrameworkCore
{
    /// <summary>
    /// 数据库上下文过滤器
    /// </summary>
    [SkipScan]
    public class UnitOfWorkFilter : IAsyncActionFilter, IOrderedFilter
    {
        /// <summary>
        /// 数据库上下文池
        /// </summary>
        private readonly IUnitOfWork _unitOfWork;
        private readonly IDbContextManager _dbContextManager;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="unitOfWork"></param>
        public UnitOfWorkFilter(IUnitOfWork unitOfWork, IDbContextManager dbContextManager)
        {
            _unitOfWork = unitOfWork;
            _dbContextManager = dbContextManager;
        }

        public int Order => 9999;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (!context.ActionDescriptor.IsControllerAction())
            {
                await next();
                _dbContextManager.CloseConnection();
                return;
            }

            var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;

            var unitOfWorkAttr = methodInfo.GetCustomAttributes(false).OfType<UnitOfWorkAttribute>().FirstOrDefault();
            if (unitOfWorkAttr == null)
            {
                await next();
                _dbContextManager.CloseConnection();
                return;
            }

            try
            {
                await _unitOfWork.BeginAsync();

                var result = await next();
                if (result.Exception == null)
                {
                    await _unitOfWork.CommitAsync();
                }
                else
                {
                    await _unitOfWork.RollbackAsync();
                }
            }
            catch (Exception ex)
            {
                App.PrintToMiniProfiler(MiniProfilerCategory.UnitOfWork.GetDescription(), $"Transaction", $"{ex}", true);

                await _unitOfWork.RollbackAsync();

                throw new Exception(ex.ToString(), ex.InnerException);
            }
            finally
            {
                _dbContextManager.CloseConnection();
            }
        }
    }
}
